// Magic Software, Inc.
// http://www.magic-software.com
// Copyright (c) 2000, All Rights Reserved
//
// Source code from Magic Software is supplied under the terms of a license
// agreement and may not be copied or disclosed except in accordance with the
// terms of that agreement.  The various license agreements may be found at
// the Magic Software web site.  This file is subject to the license
//
// RESTRICTED USE SOURCE CODE
// http://www.magic-software.com/License/restricted.pdf

#include "MgcNode.h"
#include "MgcRenderer.h"
#include "MgcSpatial.h"

MgcImplementRTTI(MgcSpatial,MgcObject);
MgcImplementStream(MgcSpatial);

//---------------------------------------------------------------------------
MgcSpatial::MgcSpatial ()
    :
    m_kRotate(MgcMatrix3::IDENTITY),
    m_kTranslate(MgcVector3::ZERO),
    m_kWorldRotate(MgcMatrix3::IDENTITY),
    m_kWorldTranslate(MgcVector3::ZERO)
{
    m_pkParent = 0;
    m_fScale = 1.0;
    m_fWorldScale = 1.0;
    m_bForceCull = false;
    m_pkStateList = 0;
}
//---------------------------------------------------------------------------
MgcSpatial::~MgcSpatial ()
{
    RemoveAllStates();
}
//---------------------------------------------------------------------------
MgcRenderStatePtr MgcSpatial::SetRenderState (MgcRenderState* pkState)
{
    assert( pkState );

    // check if type of state already exists
    MgcRenderState::List* pkList;
    for (pkList = m_pkStateList; pkList; pkList = pkList->m_pkNext)
    {
        if ( pkList->m_spkState->GetType() == pkState->GetType() )
        {
            // type of state exists, replace it
            MgcRenderState* pkSave = pkList->m_spkState;
            pkList->m_spkState = pkState;
            return pkSave;
        }
    }

    // type of state not in current list, add state
    pkList = new MgcRenderState::List;
    pkList->m_spkState = pkState;
    pkList->m_pkNext = m_pkStateList;
    m_pkStateList = pkList;
    return 0;
}
//---------------------------------------------------------------------------
MgcRenderState* MgcSpatial::GetRenderState (MgcRenderState::Type eType)
{
    // check if type of state already exists
    MgcRenderState::List* pkList;
    for (pkList = m_pkStateList; pkList; pkList = pkList->m_pkNext)
    {
        if ( pkList->m_spkState->GetType() == eType )
        {
            // type of state exists, return it
            return pkList->m_spkState;
        }
    }

    return 0;
}
//---------------------------------------------------------------------------
MgcRenderStatePtr MgcSpatial::RemoveRenderState (MgcRenderState::Type eType)
{
    // check if type of state already exists
    MgcRenderState::List* pkList;
    for (pkList = m_pkStateList; pkList; pkList = pkList->m_pkNext)
    {
        if ( pkList->m_spkState->GetType() == eType )
        {
            // type of state exists, remove it
            MgcRenderState* pkSave = pkList->m_spkState;
            pkList->m_spkState = 0;
            return pkSave;
        }
    }

    // type of state not in current list
    return 0;
}
//---------------------------------------------------------------------------
void MgcSpatial::RemoveAllStates ()
{
    while ( m_pkStateList )
    {
        m_pkStateList->m_spkState = 0;
        MgcRenderState::List* pSave = m_pkStateList->m_pkNext;
        delete m_pkStateList;
        m_pkStateList = pSave;
    }
}
//---------------------------------------------------------------------------
void MgcSpatial::UpdateWorldData (MgcReal fAppTime)
{
    // update render state controllers
    MgcRenderState::List* pkSList;
    MgcController* pkControl;
    for (pkSList = m_pkStateList; pkSList; pkSList = pkSList->m_pkNext)
    {
        MgcRenderState* pkState = pkSList->m_spkState;
        pkControl = pkState->GetControllers();
        while ( pkControl )
        {
            pkControl->Update(fAppTime);
            pkControl = pkControl->GetNext();
        }
    }

    // update spatial controllers
    bool bComputesWorldTransform = false;
    pkControl = GetControllers();
    while ( pkControl )
    {
        bComputesWorldTransform = pkControl->Update(fAppTime);
        pkControl = pkControl->GetNext();
    }

    // update world transforms
    if ( !bComputesWorldTransform )
    {
        if ( m_pkParent )
        {
            m_fWorldScale = m_pkParent->m_fWorldScale*m_fScale;
            m_kWorldRotate = m_pkParent->m_kWorldRotate*m_kRotate;
            m_kWorldTranslate = m_pkParent->m_kWorldTranslate +
                m_pkParent->m_fWorldScale*(m_pkParent->m_kWorldRotate *
                m_kTranslate);
        }
        else
        {
            m_fWorldScale = m_fScale;
            m_kWorldRotate = m_kRotate;
            m_kWorldTranslate = m_kTranslate;
        }
    }
}
//---------------------------------------------------------------------------
void MgcSpatial::PropagateBoundToRoot ()
{
    if ( m_pkParent )
    {
        m_pkParent->UpdateWorldBound();
        m_pkParent->PropagateBoundToRoot();
    }
}
//---------------------------------------------------------------------------
void MgcSpatial::UpdateGS (MgcReal fAppTime, bool bInitiator)
{
    UpdateWorldData(fAppTime);
    UpdateWorldBound();
    if ( bInitiator )
        PropagateBoundToRoot();
}
//---------------------------------------------------------------------------
void MgcSpatial::PropagateStateFromRoot (MgcRenderState* apkState[])
{
    // traverse to root to allow downward state propagation
    if ( m_pkParent )
        m_pkParent->PropagateStateFromRoot(apkState);

    // update previous state by current state
    MgcRenderState::List* pkList;
    for (pkList = m_pkStateList; pkList; pkList = pkList->m_pkNext)
    {
        MgcRenderState* pkState = pkList->m_spkState;
        MgcRenderState::Type eType = pkState->GetType();
        pkState->Update(apkState[eType]);
    }
}
//---------------------------------------------------------------------------
void MgcSpatial::UpdateRS (MgcRenderState* apkPreviousState[])
{
    MgcRenderState* apkState[MgcRenderState::RS_MAX_STATE];
    MgcRenderState::List* pkList;

    // update render states
    if ( apkPreviousState )
    {
        // initialize with previous render states
        memcpy(apkState,apkPreviousState,MgcRenderState::RS_QUANTITY);

        // Update previous state by current state.  The default light and
        // texture states act as accumulators and are changed.  They need
        // to be restored on return from the recursive traversal.
        for (pkList = m_pkStateList; pkList; pkList = pkList->m_pkNext)
        {
            MgcRenderState* pkState = pkList->m_spkState;
            MgcRenderState::Type eType = pkState->GetType();
            pkState->Update(apkState[eType]);
        }
    }
    else
    {
        // initialize with default render states
        memcpy(apkState,MgcRenderState::GetDefaultStates(),
            MgcRenderState::RS_QUANTITY);

        PropagateStateFromRoot(apkState);
    }

    UpdateRenderState(apkState);

    // restore render states
    if ( apkPreviousState )
    {
        // Restore previous state.  The default light and texture states
        // were potentially changed during the recursive traversal.  They
        // need to be restored.
        for (pkList = m_pkStateList; pkList; pkList = pkList->m_pkNext)
        {
            MgcRenderState* pkState = pkList->m_spkState;
            MgcRenderState::Type eType = pkState->GetType();
            pkState->Restore(apkState[eType]);
        }
    }
}
//---------------------------------------------------------------------------
void MgcSpatial::OnDraw (MgcRenderer& rkRenderer)
{
    if ( m_bForceCull )
        return;

    MgcCameraPtr spCamera = rkRenderer.GetCamera();
    unsigned int uiState = spCamera->GetPlaneState();

    if ( !spCamera->Culled(m_kWorldBound) )
        Draw(rkRenderer);

    spCamera->SetPlaneState(uiState);
}
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// streaming
//---------------------------------------------------------------------------
MgcObject* MgcSpatial::Factory (MgcStream& rkStream)
{
    // MgcSpatial is abstract, Factory never called
    return 0;
}
//---------------------------------------------------------------------------
void MgcSpatial::Load (MgcStream& rkStream, MgcStream::Link* pkLink)
{
    MgcObject::Load(rkStream,pkLink);

    // native data
    MgcStreamRead(rkStream,m_kRotate);
    MgcStreamRead(rkStream,m_kTranslate);
    MgcStreamRead(rkStream,m_fScale);

    // world transforms and world bound are derived, no need to save

    // link data
    unsigned int uiQuantity;
    MgcStreamRead(rkStream,uiQuantity);
    m_pkStateList = 0;
    for (unsigned int uiI = 0; uiI < uiQuantity; uiI++)
    {
        MgcRenderState* pkState;
        MgcStreamRead(rkStream,pkState);
        pkLink->Add(pkState);

        // build render state list, to be filled in by Link
        MgcRenderState::List* pkList = new MgcRenderState::List;
        pkList->m_spkState = 0;
        pkList->m_pkNext = m_pkStateList;
        m_pkStateList = pkList;
    }
}
//---------------------------------------------------------------------------
void MgcSpatial::Link (MgcStream& rkStream, MgcStream::Link* pkLink)
{
    MgcObject::Link(rkStream,pkLink);

    MgcRenderState::List* pkList;
    for (pkList = m_pkStateList; pkList; pkList = pkList->m_pkNext)
    {
        MgcObject* pkLinkID = pkLink->GetLinkID();
        pkList->m_spkState = (MgcRenderState*)rkStream.GetFromMap(pkLinkID);
    }
}
//---------------------------------------------------------------------------
bool MgcSpatial::Register (MgcStream& rkStream)
{
    if ( !MgcObject::Register(rkStream) )
        return false;

    // m_pkParent need not be registered since the parent itself must have
    // initiated the Register call to its children, 'this' being one of them.

    MgcRenderState::List* pkList;
    for (pkList = m_pkStateList; pkList; pkList = pkList->m_pkNext)
    {
        MgcRenderState* pkState = pkList->m_spkState;
        if ( pkState )
            pkState->Register(rkStream);
    }

    return true;
}
//---------------------------------------------------------------------------
void MgcSpatial::Save (MgcStream& rkStream)
{
    MgcObject::Save(rkStream);

    // native data
    MgcStreamWrite(rkStream,m_kRotate);
    MgcStreamWrite(rkStream,m_kTranslate);
    MgcStreamWrite(rkStream,m_fScale);

    // world transforms and world bound are derived, no need to save

    // link data

    // m_pkParent need not be saved since 'this' will be attached as a child
    // in MgcNode::Link.

    MgcRenderState::List* pkList;
    unsigned int uiQuantity = 0;
    for (pkList = m_pkStateList; pkList; pkList = pkList->m_pkNext)
        uiQuantity++;

    MgcStreamWrite(rkStream,uiQuantity);

    for (pkList = m_pkStateList; pkList; pkList = pkList->m_pkNext)
        MgcStreamWrite(rkStream,pkList->m_spkState);
}
//---------------------------------------------------------------------------
